/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package chatroom;

/**
 *
 * @author Caca
 */
import java.io.*;
import java.net.*;
import java.text.SimpleDateFormat;
import java.util.*;

public class Server {

    private static int uniqueId;
    private ArrayList<ClientThread> al;
    private ServerGUI sg;
    private SimpleDateFormat sdf;
    private int port;
    private boolean keepGoing;
    private String marks;
    private ChatMessage cmSend;
    private String[][] mapCo = new String[20][20];
    private int idRemove;
    private EventXML eventXML;

    public Server(int port) {
        this(port, null);
    }

    public Server(int port, ServerGUI sg) {
        this.sg = sg;
        this.port = port;
        sdf = new SimpleDateFormat("HH:mm:ss");
        al = new ArrayList<ClientThread>();
         eventXML =  new EventXML();
        idRemove = -1;
        for (int i = 0; i < 20; i++) {
            for (int j = 0; j < 20; j++) {
                mapCo[i][j] = "A";
            }
        }

    }

    public void start() {
        keepGoing = true;
        try {
            ServerSocket serverSocket = new ServerSocket(port);
            while (keepGoing) {
                display("Server đang đợi các Client trên Port: " + port + "......");
                Socket socket = serverSocket.accept();
                if (!keepGoing) {
                    break;
                }
                ClientThread t = new ClientThread(socket);
                al.add(t);
                t.start();

                // Xoa client khoi danh sach al
                if (idRemove >= 0) {
                    al.remove(idRemove);
                }

            }

            try {
                serverSocket.close();
                for (int i = 0; i < al.size(); ++i) {
                    ClientThread tc = al.get(i);
                    try {
                        tc.sInput.close();
                        tc.sOutput.close();
                        tc.socket.close();
                    } catch (IOException ioE) {
                    }
                }
            } catch (Exception e) {
                display("Exception closing the server and clients: " + e);
            }
        } catch (IOException e) {
            String msg = sdf.format(new Date()) + " Exception on new ServerSocket: " + e + "\n";
            display(msg);
        }
    }

    protected void stop() {
        keepGoing = false;
        try {
            new Socket("localhost", port);
        } catch (Exception e) {
        }
    }

    // Hien thi len text EVENT
    private void display(String msg) {
        String time = sdf.format(new Date()) + " " + msg;
        if (sg == null) {
            System.out.println(time);
        } else {
            sg.appendEvent(time + "\n");
        }
    }

    // Gui mes den tat cac client
    private synchronized void broadcast(ChatMessage cmessage, boolean isVisible) {

        String time = sdf.format(new Date());
        String messageLf = time + " " + cmessage.getMessage() + "\n";
        if (sg == null) {
            System.out.print(messageLf);
        } else if (isVisible == true && sg != null) {
            sg.appendRoom(messageLf);
        }

        // Tung thread Client write mes
        for (int i = al.size(); --i >= 0;) {
            ClientThread ct = al.get(i);

            if (!ct.writeMsg(cmessage)) {
                al.remove(i);
                display("Đứt kết nối " + ct.username + " và xóa khỏi danh sách Client.");

            }
        }
    }

    // Xoa id Client khi logout
    synchronized void remove(int id) {

        for (int i = 0; i < al.size(); ++i) {
            ClientThread ct = al.get(i);
            if (ct.id == id) {
                al.remove(i);
                return;
            }
        }
    }

    // Kiem tra WIn
    public String checkWin(String[][] banco, String type) {
        for (int i = 0; i < 20; i++) {
            for (int j = 0; j < 20; j++) {
                if (i < 16) {
                    if (banco[i][j].equals(banco[i + 1][j]) && banco[i][j].equals(banco[i + 2][j])
                            && banco[i][j].equals(banco[i + 3][j]) && banco[i][j].equals(banco[i + 4][j])
                            && banco[i][j].equals(type)) {
                        return type;
                    }
                }

                if (i < 16 && j < 16) {
                    if (banco[i][j].equals(banco[i + 1][j + 1]) && banco[i][j].equals(banco[i + 2][j + 2])
                            && banco[i][j].equals(banco[i + 3][j + 3]) && banco[i][j].equals(banco[i + 4][j + 4])
                            && banco[i][j].equals(type)) {
                        return type;
                    }
                }

                if (i > 5 && j < 16) {
                    if (banco[i][j].equals(banco[i - 1][j + 1]) && banco[i][j].equals(banco[i - 2][j + 2])
                            && banco[i][j].equals(banco[i - 3][j + 3]) && banco[i][j].equals(banco[i - 4][j + 4])
                            && banco[i][j].equals(type)) {
                        return type;
                    }
                }

                if (j < 16) {
                    if (banco[i][j].equals(banco[i][j + 1]) && banco[i][j].equals(banco[i][j + 2])
                            && banco[i][j].equals(banco[i][j + 3]) && banco[i][j].equals(banco[i][j + 4])
                            && banco[i][j].equals(type)) {
                        return type;
                    }
                }
            }
        }

        return "A";
    }


    public static void main(String[] args) {

        int portNumber = 1500;
        Server server = new Server(portNumber);
        server.start();
    }

    class ClientThread extends Thread {

        Socket socket;
        ObjectInputStream sInput;
        ObjectOutputStream sOutput;
        int id; // id tung thread
        public String username;
        ChatMessage cm;
        String date;
        public String myMarks;
        public boolean onGame;
        public String myExp;

        ClientThread(Socket socketClient) {
            id = ++uniqueId;     
            this.socket = socketClient;
            this.onGame = false;
            this.myMarks = "";
            this.myExp = "";

            System.out.println("Mở 1 Thread Client mới!!!");
            try {

                sOutput = new ObjectOutputStream(socket.getOutputStream());
                sInput = new ObjectInputStream(socket.getInputStream());
                
                // doc  username
                 username = (String) sInput.readObject();
                 display(username + " đã kết nối với Server.");
                 cm = new ChatMessage(ChatMessage.JOINGAME, username + " đã vào room");
                 broadcast(cm, true);
                // Xu ly client trung ten
                int dem = 0;
                for (int i = 0; i < al.size(); i++) {                   
                    if (al.get(i).username.equals(username)) {
                        dem++;
                        idRemove = al.size();
                    }
                    if (dem == 1) {
                        display(" Đã xảy ra trùng tên của client mới (" + username + ") đăng nhập. Đóng kết nối của client mới trùng tên???");
                        close();
                        break;
                    }                    
                    idRemove = -1;
                }

            } catch (IOException e) {
                display("Exception creating new Input/output Streams: " + e);
                return;
            } catch (ClassNotFoundException e) {
            }
            date = new Date().toString() + "\n";
        }

        public void run() {

            boolean keepGoing = true;
            while (keepGoing) {

                try {
                    cm = (ChatMessage) sInput.readObject();
                } catch (IOException e) {
                    display(username + " Exception reading Streams: " + e);
                    cmSend = new ChatMessage(ChatMessage.LOGOUT, username);
                    broadcast(cmSend, false);
                    display(username + " đã rời room");
                    break;
                } catch (ClassNotFoundException e2) {
                    break;
                }

                String message = cm.getMessage();
                // xu ly cac loai msg
                switch (cm.getType()) {
                    case ChatMessage.MESSAGE:
                        message = username + " : " + message;
                        cmSend = new ChatMessage(ChatMessage.MESSAGE, message);
                        broadcast(cmSend, true);
                        break;
                    case ChatMessage.LOGOUT:
                        display(username + " đã thoát khỏi room... ");
                        message = username;
                        cmSend = new ChatMessage(ChatMessage.LOGOUT, message);
                        broadcast(cmSend, false);
                        if (!myMarks.equals("")){
                            display("Reset lại bàn cờ...");
                            for (int i = 0; i < 20; i++) {
                                for (int j = 0; j < 20; j++) {
                                    mapCo[i][j] = "A";
                                }
                        }
                        }
                        keepGoing = false;
                        break;
                    case ChatMessage.JOINGAME:
                        
                        // myExp
                        if (eventXML.checkClient(username)) {                            
                            myExp = eventXML.getExpClient(username);
                        } else {                            
                            eventXML.themClient(username);
                            myExp = "0";
                        }
                        
                        
                        // set bien myMark
                        
                        int demX = 0,
                         demO = 0;
                        for (int i = 0; i < al.size(); i++) {
                            if (al.get(i).myMarks == "X") {
                                demX++;
                            }
                            if (al.get(i).myMarks == "O") {
                                demO++;
                            }
                        }
                        if (demX == 1 && demO == 0) {
                            myMarks = "O";
                        } else if ((demX == 0 && demO == 1) || (demX == 0 && demO == 0)) {
                            myMarks = "X";
                        } else if ((demX == 1 && demO == 1)) {
                            myMarks = "";
                            cmSend = new ChatMessage(ChatMessage.FULLGAME, "");
                            broadcast(cmSend, false);
                        }
                        // kiem tra so luong client dang o che do choi game
                        int dem = 0;
                        for (int i = 0; i < al.size(); i++) {
                            if (al.get(i).onGame == true) {
                                dem++;
                            }
                            if (dem > 2) {
                                break;
                            }
                        }
                        // set bien onGame + Start Game
                        if (dem == 1) {
                            onGame = true;
                            cmSend = new ChatMessage(ChatMessage.JOINGAME, "Start Game:");
                            broadcast(cmSend, true);
                            for (int j = 0; j < al.size(); ++j) {
                                ClientThread ct = al.get(j);
                                cmSend = new ChatMessage(ChatMessage.JOINGAME, (j + 1) + ") " + ct.username + " : " + ct.myMarks);
                                broadcast(cmSend, true);
                                cmSend = new ChatMessage(ChatMessage.EXP,ct.username +" : "+ ct.myExp);
                                broadcast(cmSend, false);
                            }
                        } else if (dem == 0) {
                            cmSend = new ChatMessage(ChatMessage.JOINGAME, "Wait for 1 Client more... ");
                            writeMsg(cmSend);
                            onGame = true;
                        } else if (dem > 1) {
                            onGame = false;
                        }


                        break;

                    case ChatMessage.MOVES:
                        cmSend = new ChatMessage(ChatMessage.MOVES, message);
                        broadcast(cmSend, true);
                        // write in mapCO
                        String row = "?";
                        String col = "?";
                        if (message.substring(5, 6).equals("X")) {
                            if (message.substring(10, 11).equals(",")) {
                                row = message.substring(9, 10);
                                if (message.length() == 12) {
                                    col = message.substring(11, 12);
                                } else if (message.length() == 13) {
                                    col = message.substring(11, 13);
                                }
                            } else if (message.substring(11, 12).equals(",")) {
                                row = message.substring(9, 11);
                                if (message.length() == 13) {
                                    col = message.substring(12, 13);
                                } else if (message.length() == 14) {
                                    col = message.substring(12, 14);
                                }
                            }
                            mapCo[Integer.parseInt(row)][Integer.parseInt(col)] = "X";
                        }
                        if (message.substring(5, 6).equals("O")) {
                            if (message.substring(10, 11).equals(",")) {
                                row = message.substring(9, 10);
                                if (message.length() == 12) {
                                    col = message.substring(11, 12);
                                } else if (message.length() == 13) {
                                    col = message.substring(11, 13);
                                }
                            } else if (message.substring(11, 12).equals(",")) {
                                row = message.substring(9, 11);
                                if (message.length() == 13) {
                                    col = message.substring(12, 13);
                                } else if (message.length() == 14) {
                                    col = message.substring(12, 14);
                                }
                            }
                            mapCo[Integer.parseInt(row)][Integer.parseInt(col)] = "O";
                        }
                        // Check Win
                        if (checkWin(mapCo,"X").equals("X")) {
                            cmSend = new ChatMessage(ChatMessage.WIN, "X Win............");
                            broadcast(cmSend, true);
                            for (int i = 0; i<al.size(); i++) {
                                if(al.get(i).myMarks.equals("X")){                                   
                                    eventXML.capNhatExpClient(al.get(i).username, 10);
                                    display(al.get(i).username+ " được cộng 10 điểm kinh nghiệm ");
                                    break;
                                }
                            }
                        }
                        if (checkWin(mapCo, "O").equals("O")) {
                            cmSend = new ChatMessage(ChatMessage.WIN, "O Win.............");
                            broadcast(cmSend, true);
                             for (int i = 0; i<al.size(); i++) {
                                if(al.get(i).myMarks.equals("O")){                                     
                                    eventXML.capNhatExpClient(al.get(i).username, 10);
                                    display(al.get(i).username+ " được cộng 10 điểm kinh nghiệm ");
                                    break;
                                }
                            }
                        }
                        break;
                    case ChatMessage.WIN:
                        break;
                    case ChatMessage.AGAIN:
                        // thiet lap choi lai
                        cmSend = new ChatMessage(ChatMessage.AGAIN, "Play Again ");
                        broadcast(cmSend, true);
                        for (int i = 0; i < 20; i++) {
                            for (int j = 0; j < 20; j++) {
                                mapCo[i][j] = "A";
                            }
                        }
                        break;
                    case ChatMessage.ISWRITE:
                        cmSend = new ChatMessage(ChatMessage.ISWRITE, username);
                        broadcast(cmSend, false);
                        break;
                    case ChatMessage.ISWRITESTOP:
                        cmSend = new ChatMessage(ChatMessage.ISWRITESTOP, username);
                        broadcast(cmSend, false);
                        break;
                    case ChatMessage.LOADGAME:
                        for (int i = 0; i < 20; i++) {
                            for (int j = 0; j < 20; j++) {
                                if (!mapCo[i][j].equals("A")){
                                     cmSend = new ChatMessage(ChatMessage.LOADGAME,mapCo[i][j] +":("+i+","+j+")");
                                     broadcast(cmSend, false);                                     
                                }
                            }
                        }
                        break;
                     case ChatMessage.EXP:
                         break;
                }
            }
            // xoa client id ra khoi array list
            remove(id);
            close();
        }

        // khi khong can ket noi nua
        private void close() {

            try {
                if (sOutput != null) {
                    sOutput.close();
                }
            } catch (Exception e) {
            }
            try {
                if (sInput != null) {
                    sInput.close();
                }
            } catch (Exception e) {
            };
            try {
                if (socket != null) {
                    socket.close();
                }
            } catch (Exception e) {
            }
        }

        // Viet mes len server
        private boolean writeMsg(ChatMessage msg) {

            if (!socket.isConnected()) {
                close();
                return false;
            }
            try {
                sOutput.writeObject(msg);
            } catch (IOException e) {
                display("Error sending message to " + username);
                display(e.toString());
            }
            return true;
        }
    }
}
